
local ffi = FFI

Group = {}

function Group.new()
	local o = {
		--state = ffi.new("long long", 0),
		state = 0,
		--state_num = ffi.new("long long", 0),
		state_num = 0,
		--in_queue = ffi.new("bool", false),
		--in_queue = false,
		--in_queue = ffi.new("long long", 0),
		in_queue = 0,
		gates_update = {},
		num_gates_update = 0,
		
		fxstate = 0,
		update_tick = 0,
		
		wires = {},
		out_ports = {},
		in_ports = {},
		nwires = 0,
		nout_ports = 0,
		nin_ports = 0,
	}
	return o
end

function Group.getsize(group)
	return group.nwires + group.nout_ports + group.nin_ports
end

function Group.addwire(group, wire)
	if Wire.getgroup(wire) ~= group then
		if Wire.getgroup(wire) ~= nil then
			Group.mergewith(group, Wire.getgroup(wire))
		else
			group.wires[wire] = wire
			group.nwires = group.nwires + 1
			
			Wire.setgroup(wire, group)
			Wire.update(wire)
			
			Simulation.queuegroup(GSim, group)
		end
	end
end

function Group.removewire(group, wire)
	Wire.setgroup(wire, nil)
	group.wires[wire] = nil
	
	local sim = GSim
	
	for k, wire in pairs(group.wires) do
		Wire.setgroup(wire, nil)
	end
	
	for k, port in pairs(group.out_ports) do
		Port.setgroup(port, nil)
	end
	
	for k, port in pairs(group.in_ports) do
		Port.setgroup(port, nil)
	end
	
	for k, wire in pairs(group.wires) do
		Simulation.connectwire(sim, wire)
	end
	
	for k, port in pairs(group.out_ports) do
		Simulation.connectport(sim, port)
	end
	
	for k, port in pairs(group.in_ports) do
		Simulation.connectport(sim, port)
	end
	
	group.wires = {}
	group.out_ports = {}
	group.in_ports = {}
	
	group.nwires = 0
	group.nout_ports = 0
	group.nin_ports = 0
	
	Simulation.dequeuegroup(GSim, group)
end

function Group.addport(group, port)
	if port.group~=nil then error("port already has group") end
	Port.setgroup(port, group)
	
	if port.type == PortTypes.output then
		if group.out_ports[port] then error("port already in group") end
		
		group.out_ports[port] = port
		group.nout_ports = group.nout_ports + 1
		group.state_num = group.state_num + Port.getstate(port)
		
		Simulation.queuegroup(GSim, group)
		
	elseif port.type == PortTypes.input then
		if group.in_ports[port] then error("port already in group") end
		
		group.in_ports[port] = port
		group.nin_ports = group.nin_ports + 1
		
		Simulation.queuegate(GSim, Port.getgate(port))
		
	end
	
	Group.rebuild_ports(group)
end

function Group.removeport(group, port)
	if port.group~=group then error("port does not have group") end
	Port.setgroup(port, nil)
	
	if port.type == PortTypes.output then
		if not group.out_ports[port] then error("port not in group") end
		group.out_ports[port] = nil
		group.nout_ports = group.nout_ports - 1
		
		group.state_num = group.state_num - Port.getstate(port)
		
		Simulation.queuegroup(GSim, group)
		
	elseif port.type == PortTypes.input then
		if not group.in_ports[port] then error("port not in group") end
		
		group.in_ports[port] = nil
		group.nin_ports = group.nin_ports - 1
		
		Simulation.queuegate(GSim, Port.getgate(port))
	end
	
	Group.rebuild_ports(group)
end

function Group.mergewith(group, group2)
	if Group.getsize(group) >= Group.getsize(group2) then
		Group.mergeinto(group2, group)
		return group
	else
		Group.mergeinto(group, group2)
		return group2
	end
end

function Group.mergeinto(group, group2)
	for k, wire in pairs(group.wires) do
		Wire.setgroup(wire, nil)
		Group.addwire(group2, wire)
	end

	for k, port in pairs(group.out_ports) do
		Port.setgroup(port, nil)
		Group.addport(group2, port)
	end

	for k, port in pairs(group.in_ports) do
		Port.setgroup(port, nil)
		Group.addport(group2, port)
	end
	
	group.wires = {}
	group.out_ports = {}
	group.in_ports = {}

	group.nwires = 0
	group.nout_ports = 0
	group.nin_ports = 0
	
	Simulation.dequeuegroup(GSim, group)
end

-- Logic Critical
function Group.setstate(group, state)
	if state ~= group.state then
		local sim = GSim
		
		group.state = state
		group.update_tick = sim.current_tick
		
		--for k, gate in ipairs(group.gates_update) do
		local len = group.num_gates_update
		for i = 1, len do
			local gate = group.gates_update[i]
			Simulation.queuegate(sim, gate)
		end
		
		Simulation.queuegroupfx(sim, group)
	end
end

function Group.update(group)
	Group.setstate(group, group.state_num>0 and 1 or 0)
end

function Group.rebuild_ports(group)
	group.gates_update = {}
	group.num_gates_update = 0
	for k, port in pairs(group.in_ports) do
		if port.causeupdate then
			array_add(group.gates_update, Port.getgate(port))
			group.num_gates_update = group.num_gates_update + 1
		end
	end
end
